Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove inline Javascript, part I #9513

Open
wants to merge 56 commits into
base: master
Choose a base branch
from

Conversation

pabzm
Copy link
Member

@pabzm pabzm commented Jun 21, 2024

As part of #9508 here's a change that removes add_script() and transports js_commands in JSON, which then gets interpreted as callbacks to call with given arguments, instead of evaling strings.

This does not tackle the many inline event handlers, in order to keep it manageable.

@pabzm pabzm changed the title Extract inline JS code, to allow stricter CSPs Remove inline Javascript, part I Jun 21, 2024
@pabzm
Copy link
Member Author

pabzm commented Jun 21, 2024

@alecpl I don't know yet why the tests fail, will have a look at that on monday. But if you have the time maybe you can already have a look if you (roughly) agree?

@pabzm pabzm force-pushed the extract-inline-js branch 6 times, most recently from c8efaeb to abe9212 Compare June 24, 2024 10:19
@pabzm pabzm self-assigned this Jun 24, 2024
@pabzm pabzm force-pushed the extract-inline-js branch 6 times, most recently from 1bce625 to 965e50e Compare June 25, 2024 15:37
@pabzm pabzm marked this pull request as ready for review June 25, 2024 15:47
@pabzm pabzm requested a review from alecpl June 25, 2024 15:47
@pabzm
Copy link
Member Author

pabzm commented Jul 8, 2024

@alecpl Could you have a look at this, please? I'll go forward and work on extracting inline event handlers, but I'd like to know as early as possible about vetos from your side.

Copy link
Member

@alecpl alecpl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some preliminary comments. Overall not bad.

program/js/rcmail-init.js Outdated Show resolved Hide resolved
program/js/googiespell_init.js Outdated Show resolved Hide resolved
}
} catch (e) { }
</script>
<script src="./dark-mode.js"></script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably would be better to move to ui.js.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then we lose the conditional in layout.html: https://github.com/pabzm/roundcubemail/blob/965e50ea084d799bc0b337e542ba32054dec582e/skins/elastic/templates/includes/layout.html#L31
Do you want to drop that?

Also using us.js as dependency in watermark.html doesn't look good, I think.

Even if you want to reduce the amount of JS-files I'd argue that this code snippet is really well suited for its own file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Less files is better. If the options are load dark-mode.js v. load ui.js, I choose ui.js (it will be already cached by the browser).

program/include/rcmail_output_html.php Outdated Show resolved Hide resolved
program/actions/mail/compose.php Outdated Show resolved Hide resolved
program/actions/settings/index.php Outdated Show resolved Hide resolved
program/include/rcmail_action.php Outdated Show resolved Hide resolved
if (!empty($this->script_files['foot'])) {
$page_footer .= array_reduce((array) $this->script_files['foot'], $merge_script_files);
}
$page_footer .= html::div(['id' => 'js-data', 'style' => 'display: none', 'hidden' => true], $this->get_js_commands());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to encode the div content here. E.g. to not contain < and >, etc. Then I think we should be able to do .text() on the client side.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather go with the current code, because it's safe enough (JSON-encoded), and encoding and then decoding everything adds a (maybe small but still) performance weight. Also, it works, why change it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While doing the requested changes the data handling broke again, so I decided to use a data-attribute instead. That works for all cases.

program/include/rcmail_output_html.php Outdated Show resolved Hide resolved
@pabzm pabzm force-pushed the extract-inline-js branch 2 times, most recently from a2b0156 to e8036cf Compare July 9, 2024 17:27
@pabzm
Copy link
Member Author

pabzm commented Jul 9, 2024

I rebased onto the latest master to get rid of the unrelated phpstan errors, and then force-pushed. Sorry if that muddles up your review process.

@pabzm pabzm requested a review from alecpl July 9, 2024 17:34
program/actions/mail/compose.php Outdated Show resolved Hide resolved
program/js/editor.js Outdated Show resolved Hide resolved
* @param string $script JS code snippet
* @param string $position Target position [head|head_top|foot|docready]
*/
public function add_script($script, $position = 'head')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of removing the API we should consider using the nonce for these. It would make the plugin developers life easier. However, there will be problem with execution order, I suppose, so I'm not sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to not offer that option, because it's cleaner without it, and I guess most plugin authors wouldn't bother to clean up their code if it still works, which factually will degrade the CSP-related security of most Roundcube installations.
As you see in this branch, converting code to use external script files isn't really hard. It's just some chores to modernise ones code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the potential problems with code order you mentioned might well result in a steady stream of issues, which we would need to take care of – that's a work load I would very much like to avoid, especially since Roundcube only loses security advancements from it.

@alecpl
Copy link
Member

alecpl commented Jul 14, 2024

Another case to figure out is rcmail_html_page class. It's used to display attachment contents, i.e. without the rcmail client context, but contains onclick handler. I think it might be a case for nonce based solution. On the other hand it's an iframe some maybe it could be handled by the parent in onload.

@pabzm
Copy link
Member Author

pabzm commented Jul 16, 2024

This isn't meant to completely remove all inline-JS at once, that would be too big a change, I figured. It's only the first part, as the title says.

I already made some progress on further changes to take care of event-handlers (which led to #9544), but I'd like this to be merged before I open another PR to avoid overload.

@alecpl
Copy link
Member

alecpl commented Jul 16, 2024

Well, this is one big BC break. I'd rather see it completed before we make the decision to merge. There will be no return.

This allows to call it on specific elements only, e.g. after they've
been inserted late to the DOM.
There's no apparent reason for them to be static, and no explanation,
but as instance methods they are directly callable from the de-inlined
event-handlers and we save some helper methods, which is good.
This make it easier for the calling code.
Have to repeat attaching event handlers after a clone().
This allows to strip 'unsafe-eval' from the CSP.
innerHTML requires 'unsafe-eval' in the CSP, while innerText doesn't.
If the last argument to a data-on* attribute is an object (associative
array in PHP), it is used as options, which allow to specify if
preventDefault() should be called on the event.
This is relevant for some parts of the code and got lost in previous
changes.
@pabzm
Copy link
Member Author

pabzm commented Sep 5, 2024

(Rebased to latest of "master")

@pabzm
Copy link
Member Author

pabzm commented Oct 30, 2024

@alecpl I would really appreciate if you would take the time to reply again on this topic. Just getting the ball dropped is a pretty frustrating collaboration experience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support CSP nonces in message frames so that unsafe-inline isn't required.
2 participants